/**
 * @file nvm.c
 * @author Linquan
 * @brief 增加判断是否进行过 NvmConfig 函数配置
 * @version 0.1
 * @date 2024-02-26
 * 
 * @copyright Copyright (c) 2024
 * 
 */

#include "nvm.h"
#include <stdio.h>

/////添加的存储头文件////////

#include "hardware_in_flash.h"
#include "hardware_out_flash.h"

//////////////////////////

#define NVM_DEIVICE_MAX_NUMBER 4
#ifndef NVM_DEIVICE_NUMBER
#define NVM_DEIVICE_NUMBER NVM_DEIVICE_MAX_NUMBER
#endif

#if NVM_DEIVICE_NUMBER > NVM_DEIVICE_MAX_NUMBER
    #error "NVM_DEIVICE_NUMBER"
#endif 

#ifndef ARRAY_SIZE
#define ARRAY_SIZE(array)   sizeof(array)/sizeof(array[0])
#endif

# define NO_CONFIG      0X12
# define NVM_CONFIGED   0XAB

#define NVM_INDEX_ADDR_MASK (uint32_t)0xC0000000
#define USR_ADDR(addr) (uint32_t)((addr) & 0x3FFFFFFF)

#define NVM_CONFIG_PRINT 1
static uint8_t NvmConfgFlag = NO_CONFIG; 

static const STR_DATA_INDEX* pDataIndexConfg = NULL; 

static STR_NVM_OPERATIONS NVMOperations[NVM_DEIVICE_NUMBER] = { 
    { NULL, NULL, NULL, NULL },    
#if NVM_DEIVICE_NUMBER >= 2
    { NULL, NULL, NULL, NULL }, 
#endif 
#if NVM_DEIVICE_NUMBER >= 3
    { NULL, NULL, NULL, NULL }, 
#endif 
#if NVM_DEIVICE_NUMBER >= 4
    { NULL, NULL, NULL, NULL }, 
#endif 
};



//////////对于存储地址的安排,根据需求更改，索引号在头文件里//////////


#define IN_RECORD_LEN     (IN_FLASH_BUFF_SIZE)
#define OUT_RECORD_LEN    (OUT_FLASH_BUFF_BLOCK_NUM*OUT_FLASH_BLOCK_SIZE)

const STR_DATA_INDEX  gstDataIndexTable[DINDEX_MAX_NUM] = 
{
// NVM1: in flash  
    {DIDEX_RECORD_INDEX , 		            NVM0_ADDR(0X80EFFF),    1024 },// TEST example
	// {DIDEX_IN_ALARM_RECORD ,                NVM0_ADDR(IN_ALARM_ADDR),       IN_RECORD_LEN },
	// {DIDEX_IN_FIRST_FIRE_RECORD , 		    NVM0_ADDR(IN_FIRST_FIRE_ADDR),    IN_RECORD_LEN },
	// {DIDEX_IN_FAULT_RECORD ,                NVM0_ADDR(IN_FAULT_ADDR),    IN_RECORD_LEN },
	// {DIDEX_IN_OTHER_RECORD , 		        NVM0_ADDR(IN_OTHER_ADDR),    IN_RECORD_LEN },
	
// NVM1: out flash
    // { DIDEX_OUT_ALARM_RECORD,               NVM1_ADDR(OUT_ALAARM_ADDR),       OUT_RECORD_LEN },   
    // { DIDEX_OUT_FIRST_FIRE_RECORD,          NVM1_ADDR(OUT_FIRST_FIRE_ADDR),   OUT_RECORD_LEN },
    // { DIDEX_OUT_FAULT_RECORD,               NVM1_ADDR(OUT_FAULT_ADDR),        OUT_RECORD_LEN },
    // { DIDEX_OUT_OTHER_RECORD,               NVM1_ADDR(OUT_OTHER_ADDR),        OUT_RECORD_LEN },   
 
};

//////////配置 存储的底层驱动函数，填入下方结构体数组里//////////
static const STR_NVM_OPERATIONS gstNvmOperationsTable[] = 
{
    //NVM0: 内部flash
    {   
		 NULL,// flash_init,
		 in_flash_erase,// flash_erase_physical,
         in_flash_read,// flash_read_physical,
		 in_flash_write,// flash_write_physical, 
    },
    //NVM1: M24C64
    {

        NULL,//初始化
		out_flash_erase,//擦写
        out_flash_read,//M24M_ReadBuff,
        out_flash_write,//M24M_WriteBuff
    },
    //NVM2: 
    {   			 
        NULL,//W25_FLASH_Init,
		NULL,
        NULL,//W25N01G_Read,
        NULL,//W25N01G_Write
    },
};






//NVM模块的函数注册
uint8_t NVMRegist(uint8_t ucNvmIndex, const STR_NVM_OPERATIONS *ptr) 
{
    if (ucNvmIndex >= NVM_DEIVICE_NUMBER) 
    {
        NvmConfgFlag = NO_CONFIG;
        return 1;
    } 
    else 
    {
        NVMOperations[ucNvmIndex] = *ptr;
        return 0;
    }
}

//NVM的索引初始化，送入设置好的索引，每一部分地址都存的什么东西
uint8_t NVMInit(const STR_DATA_INDEX *ptr)
{
    uint8_t i;
    if (ptr == NULL) 
    {
        NvmConfgFlag = NO_CONFIG;
        return 1;
    } 
    else 
    {
        pDataIndexConfg = ptr;
    }
    for (i = 0; i < NVM_DEIVICE_NUMBER; i++) 
    {
        if (NVMOperations[i].pfnInit != NULL) 
        {
            NVMOperations[i].pfnInit();    
        }
		
    }
    return 0;    
}




/*******************************************************************
函数名称：NvmConfigCheck
函数原型：uint8_t NvmConfigCheck(void)
功能描述：NVM模块配置检查
补充信息：在调用函数前需要检查是否调用NvmConfig进行初始化配置
输入1   ：
输出1   ：
输出2   ：
返回    ：
*********************************************************************/
uint8_t NvmConfigCheck(void)
{
    if(NvmConfgFlag != NVM_CONFIGED) 
    {
        #if NVM_CONFIG_PRINT
        printf("NVM is not config !!!\n");
        #endif
        return 1;
    }

    return 0;

}


/*******************************************************************
函数名称：NvmConfig
函数原型：uint8_t NvmConfig(void)
功能描述：NVM模块配置
补充信息：在外调用该函数对NVM进行初始化
输入1   ：
输出1   ：
输出2   ：
返回    ：
*********************************************************************/
 uint8_t NvmConfig(void)
{
    uint8_t ucRet = 0;
    uint8_t ucDevIndex;

    NvmConfgFlag = NVM_CONFIGED;

    for (ucDevIndex = 0; ucDevIndex < ARRAY_SIZE(gstNvmOperationsTable); ucDevIndex++) 
    {
        if (!NVMRegist(ucDevIndex, &gstNvmOperationsTable[ucDevIndex])) 
        {
            ucRet = ucDevIndex + 1;
        }
    }
    if (!NVMInit(gstDataIndexTable))
    {
        ucRet = 5;
    }
  
    return ucRet;
}


uint8_t NVMErase(uint8_t ucIndex)
{
	uint8_t bRet;
    uint8_t ucNvmIndex;
    const STR_DATA_INDEX *p;

    NvmConfigCheck();

    p = pDataIndexConfg + ucIndex;
    ucNvmIndex = (uint8_t)(p->uwAddr >> 30);
    
    if (ucNvmIndex >= NVM_DEIVICE_NUMBER) 
    {
		printf ("read erro big \n");
        bRet = 1;
    } 
    else if (NVMOperations[ucNvmIndex].pflash_erase == NULL) 
    {
		printf ("read erro NULL \n");
        bRet = 1;
    } 
    else 
    {
        bRet = NVMOperations[ucNvmIndex].pflash_erase(USR_ADDR(p->uwAddr));
    }
    return bRet;
}



//NVM读取
uint8_t NVMRead(uint8_t ucIndex, void *ptr, uint16_t usLength)
{
    uint8_t bRet;
    uint8_t ucNvmIndex;
    const STR_DATA_INDEX *p;
    
    NvmConfigCheck();

    p = pDataIndexConfg + ucIndex;
    ucNvmIndex = (uint8_t)(p->uwAddr >> 30);
   
    if (ucNvmIndex >= NVM_DEIVICE_NUMBER) 
    {
		printf ("read erro big \n");
        bRet = 1;
    } 
    else if (NVMOperations[ucNvmIndex].pfnRead == NULL) 
    {
		printf ("read erro NULL \n");
        bRet = 1;
    } 
    else 
    {
        bRet = NVMOperations[ucNvmIndex].pfnRead(USR_ADDR(p->uwAddr), (uint8_t*)ptr, usLength);
    }
    return bRet;
}

//NVM写入
uint8_t NVMWrite(uint8_t ucIndex, void *ptr, uint16_t usLength)
{
    uint8_t bRet;
    uint8_t ucNvmIndex;
    const STR_DATA_INDEX *p;
    
    NvmConfigCheck();

    p = pDataIndexConfg + ucIndex;
    ucNvmIndex = (uint8_t)(p->uwAddr >> 30);
    
    if (ucNvmIndex >= NVM_DEIVICE_NUMBER) 
    {
		printf ("write erro big \n");
        bRet = 1;
    } 
    else if (NVMOperations[ucNvmIndex].pfnWrite == NULL) 
    {
		printf ("write erro NULL \n");
        bRet = 1;
    } 
    else 
    {
        bRet = NVMOperations[ucNvmIndex].pfnWrite(USR_ADDR(p->uwAddr), (uint8_t*)ptr, usLength);
    }
    return bRet;    
}

//获取索引中某一个数据的数据长度
uint32_t NVMGetLengthByIndex(uint8_t ucIndex)
{
    return (pDataIndexConfg + ucIndex)->uwLen;
}
//获取索引中某一个数据的存放地址
uint32_t NVMGetAddrByIndex(uint8_t ucIndex)
{
    const STR_DATA_INDEX *p;
    p = pDataIndexConfg + ucIndex;
    return USR_ADDR(p->uwAddr);
}



uint8_t NVMEraseByOffset(uint8_t ucIndex, uint32_t uwOffset)
{
    uint8_t ucNvmIndex;
    uint8_t bRet =0;
    const STR_DATA_INDEX *p;
    
    NvmConfigCheck();

    p = pDataIndexConfg + ucIndex;        
    ucNvmIndex = (uint8_t)(p->uwAddr >> 30);
    
    if (ucNvmIndex >= NVM_DEIVICE_NUMBER) 
    {
        bRet = 1;
    } 
    else if (NVMOperations[ucNvmIndex].pfnRead == NULL) 
    {
        bRet = 1;
    } else 
    {
        bRet = NVMOperations[ucNvmIndex].pflash_erase(USR_ADDR(p->uwAddr + uwOffset));
    }
    return bRet;
}





//偏移一定的位置去读取
uint8_t NVMReadByOffset(uint8_t ucIndex, uint32_t uwOffset, void *ptr, uint16_t usLength)
{
    uint8_t ucNvmIndex;
    uint8_t bRet =0;
    const STR_DATA_INDEX *p;
    
    NvmConfigCheck();

    p = pDataIndexConfg + ucIndex;        
    ucNvmIndex = (uint8_t)(p->uwAddr >> 30);
    
    if (ucNvmIndex >= NVM_DEIVICE_NUMBER) 
    {
        bRet = 1;
    } 
    else if (NVMOperations[ucNvmIndex].pfnRead == NULL) 
    {
        bRet = 1;
    } else 
    {
        bRet = NVMOperations[ucNvmIndex].pfnRead(USR_ADDR(p->uwAddr + uwOffset), (uint8_t*)ptr, usLength);
    }
    return bRet;
}

//偏移一定的位置去写入
uint8_t NVMWriteByOffset(uint8_t ucIndex, uint32_t uwOffset, void *ptr, uint16_t usLength)
{
    uint8_t ucNvmIndex;
    uint8_t bRet;
    const STR_DATA_INDEX *p;
    
    NvmConfigCheck();

    p = pDataIndexConfg + ucIndex;        
    ucNvmIndex = (uint8_t)(p->uwAddr >> 30);
    
    if (ucNvmIndex >= NVM_DEIVICE_NUMBER) 
    {
        return 1;
    } 
    else if (NVMOperations[ucNvmIndex].pfnWrite == NULL) 
    {
        return 1;
    } 
    else 
    {
        bRet = NVMOperations[ucNvmIndex].pfnWrite(USR_ADDR(p->uwAddr + uwOffset), (uint8_t*)ptr, usLength);
    }
    return bRet;
}


#if USE_IN_LINUX
/**
 * @brief use in linux ,to print the data in addr
 * 
 * @param ucIndex 
 * @param uwOffset 
 * @param usLength 
 * @return uint8_t 
 */
uint8_t NVM_flash_print(uint8_t ucIndex, uint32_t uwOffset, uint16_t usLength)
{
    #define TEMP_ARRY_SIZE  32

    uint8_t ucNvmIndex;
    uint8_t bRet =0;
    uint8_t temp_read_buf[TEMP_ARRY_SIZE] = {0}; 
    const STR_DATA_INDEX *p;

    uint32_t addr_start ,addr_end ; 
    uint32_t cycle_num ;
 
    NvmConfigCheck();

    p = pDataIndexConfg + ucIndex;        
    ucNvmIndex = (uint8_t)(p->uwAddr >> 30);
    
     addr_start = USR_ADDR(p->uwAddr + uwOffset);
     addr_end  =  addr_start + usLength; 
     cycle_num = usLength%TEMP_ARRY_SIZE == 0? (usLength/TEMP_ARRY_SIZE) : ((usLength/TEMP_ARRY_SIZE)+1);
 
    if (ucNvmIndex >= NVM_DEIVICE_NUMBER) 
    {
        bRet = 1;
    } 
    else if (NVMOperations[ucNvmIndex].pfnRead == NULL) 
    {
        bRet = 1;
    } else 
    {
        for(int i = 0; i < cycle_num; i++)
        {
            bRet = NVMOperations[ucNvmIndex].pfnRead(addr_start, (uint8_t*)temp_read_buf, TEMP_ARRY_SIZE);
            
            addr_start += TEMP_ARRY_SIZE;

            if(!bRet)
            {               
                for(int j = 0; j < TEMP_ARRY_SIZE; j++)
                {
                    printf("%.2d ",temp_read_buf[j]);
                }
                printf("\n");
            }                   
        }
    
    }
    return bRet;

}

#endif


NVM_INTERFACE StrNvmOperaTable =
{
	NvmConfig,
	NVMErase,
	NVMRead,
	NVMWrite,
	NVMGetLengthByIndex,
	NVMGetAddrByIndex,
	NVMEraseByOffset,
	NVMReadByOffset,
	NVMWriteByOffset,
    NVM_flash_print,
};




